﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.AddIn;
using RootfusAddinView;
using ROOTNET.Interface;

namespace GeneralHistogramOperations.TDivideAddIn
{
    /// <summary>
    /// An addin that will do division of two histograms.
    /// </summary>
    [AddIn("Divide Two Histograms", Description="Takes two histograms as input, and divides them, possibly tracking the errors")]
    public class TDivideAddIn : RootfusSurfaceItem
    {
        /// <summary>
        /// Deafult dtor.
        /// </summary>
        public TDivideAddIn()
        {
            /// TODO: Create any input or output pins that are static.
            _input_pins.Add(_numer);
            _input_pins.Add(_denom);
            _output_pins.Add(_result);
            
            /// TODO: Configure default option settings (before you load from
            /// XML.
        }

        public bool CalculateErrors { get; set; }

        public class OurState
        {
            public bool _calc_errors = false;
        }

        private OurState _cached_state = new OurState();

        private void CacheState()
        {
            _cached_state._calc_errors = CalculateErrors;
        }

        private bool StateDifferentFromCache()
        {
            return _cached_state._calc_errors != CalculateErrors;
        }

        /// <summary>
        /// Cache the local copy of the user interface. Best to
        /// reference SurfaceUI property as that will make sure
        /// it is created.
        /// </summary>
        private TDivideAddInUINormal _ui_norm = null;

        /// <summary>
        /// Returns the normal UI. Only create one per addin instance! If you get asked
        /// for it multiple times return the same one!
        /// </summary>
        public override System.Windows.FrameworkElement SurfaceUI
        {
            get {
                if (_ui_norm == null)
                {
                    _ui_norm = new TDivideAddInUINormal();
                }
                return _ui_norm;
            }
        }

        /// <summary>
        /// Return the config UI. Return a new one each time, or keep
        /// track -- whatever is simpler.
        /// </summary>
        public override System.Windows.FrameworkElement ConfigUI
        {
            get
            {
                TDivideAddInUIConfig ui = new TDivideAddInUIConfig();
                ui.DataContext = this;
                CacheState();
                return ui;
            }
        }

        /// <summary>
        /// The config has closed. See if our options changed. If so, update things!
        /// </summary>
        public override void ConfigDone()
        {
            if (StateDifferentFromCache())
            {
                CacheState();
                Callback.UpdateSaveObject(_cached_state);
                Update();
            }
        }

        /// <summary>
        /// Our settings are getting set, externally.
        /// </summary>
        /// <param name="xml_config"></param>
        public override void SetConfigXML(string xml_config)
        {
            _cached_state = Callback.ConvertXMLToSettings<OurState>(xml_config);
            CalculateErrors = _cached_state._calc_errors;
        }

        /// <summary>
        /// If you don't need a config -- then get rid fo the ConfigUI and change the
        /// return to false.
        /// </summary>
        public override bool HasSettingsUI
        {
            get { return true; }
        }

        /// <summary>
        /// The numerator and denominator pins.
        /// </summary>
        class TROOTHistPinName : PinDefinition
        {
            string _name;
            public TROOTHistPinName(string name)
            {
                _name = name;
            }
            public override string Name
            {
                get { return _name; }
            }
        }

        private List<PinDefinition> _input_pins = new List<PinDefinition>();
        private TROOTHistPinName _numer = new TROOTHistPinName("Numerator Histogram");
        private TROOTHistPinName _denom = new TROOTHistPinName("Denomenator Histogram");

        private List<string> _output_pins = new List<string>();
        private string _result = "Result Histogram";

        /// <summary>
        /// Hold onto the result histogram.
        /// </summary>
        private NTH1 _result_hist = null;

        /// <summary>
        /// What are our input pins? Make some custom ones based on
        /// PinDefinition as your parent class. It is normal to create this
        /// list just once.
        /// </summary>
        public override IList<PinDefinition> InputPins
        {
            get { return _input_pins; }
        }

        /// <summary>
        /// What are our output pins? Similar to before. It is normal
        /// to create a list once.
        /// </summary>
        public override IList<string> OutputPinNames
        {
            get { return _output_pins; }
        }

        /// <summary>
        /// Rootfus thinks inputs have changed. Recalc and if things have changed,
        /// send the update down the line.
        /// </summary>
        public override void Update()
        {
            _result_hist = null;

            ///
            /// Get input root objects and convert them to 1D histos
            /// 

            NTH1 n = Callback.GetConnectedPinDataAsTObject(_numer) as NTH1;
            NTH1 d = Callback.GetConnectedPinDataAsTObject(_denom) as NTH1;

            if (n == null || d == null)
            {
                return;
            }

            ///
            /// Now -- lets see if we can divide them!
            /// 

            _result_hist = n.Clone() as NTH1;
            if (CalculateErrors)
            {
                _result_hist.Sumw2();
            }
            _result_hist.Divide(d);
            
            /// If you have output pins, and they would have been updated
            Callback.OutputPinsChanged();
        }

        /// <summary>
        /// Someone want some info from us -- return it!
        /// </summary>
        /// <param name="pin"></param>
        /// <returns></returns>
        public override NTObject GetPinDataAsTObject(string pin)
        {
            if (pin != _result)
            {
                return null;
            }
            return _result_hist;
        }
    }
}
